﻿using Xejen.Communication.Configuration;
using Xejen.Communication.Http.Abstract;
using Xejen.Communication.Http.Configuration;
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
using System.IO;
using System.Net;
using System.Net.Http;
using System.Net.Http.Headers;
using System.Text;
using System.Text.Json;
using System.Threading;
using System.Threading.Tasks;

namespace Xejen.Communication.Http
{
    /// <summary>
    /// 代表以<see langword="WebRequest"/>驱动方式向远程服务器发起请求
    /// </summary>
    /// <creator>marc</creator>
    internal class WebRequestInvoker : IHttpInvoker
    {
        /// <inheritdoc/>
        public HttpConfig Config { get; set; }

        ///// <summary>
        ///// 令牌
        ///// </summary>
        //public string Token { get; set; }

        /// <summary>
        /// 请求头
        /// </summary>
        public NameValueCollection Headers { get; set; }

        /// <inheritdoc cref="WebRequestInvoker"/>
        protected WebRequestInvoker() { }

        ///// <inheritdoc cref="WebRequestInvoker"/>
        //public WebRequestInvoker(HttpConfig config, string token = default, NameValueCollection headers = default)
        //{
        //    Check.NotNull(config, nameof(config));

        //    Config = config;

        //    Token = token;
        //    Headers = headers;
        //}


        /// <inheritdoc cref="WebRequestInvoker"/>
        public WebRequestInvoker(HttpConfig config, NameValueCollection headers)
        {
            Check.NotNull(config, nameof(config));

            Config = config;

            Headers = headers;
        }

        /// <inheritdoc/>
        public async Task<HttpResponse> DeleteAsync(string uri, int timeout, CancellationToken cancellationToken)
        {
            return await SendAsync(uri, "DELETE", null, timeout, cancellationToken);
        }

        /// <inheritdoc/>
        public async Task<HttpResponse> GetAsync(string uri, int timeout, CancellationToken cancellationToken)
        {
            return await SendAsync(uri, "GET", null, timeout, cancellationToken);
        }

        /// <inheritdoc/>
        public async Task<HttpResponse> PostAsync(string uri, object data, int timeout, CancellationToken cancellationToken)
        {
            return await SendAsync(uri, "POST", data, timeout, cancellationToken);
        }

        /// <inheritdoc/>
        public async Task<HttpResponse> PutAsync(string uri, object data, int timeout, CancellationToken cancellationToken)
        {
            return await SendAsync(uri, "PUT", data, timeout, cancellationToken);
        }

        private async Task<HttpResponse> SendAsync(string uri, string method, object data, int timeout, CancellationToken cancellationToken)
        {
            cancellationToken.ThrowIfCancellationRequested();

            WebRequest request = WebRequest.Create(uri);
            request.Method = method;

            if (Headers != null)
            {
                foreach (string key in Headers.Keys)
                {
                    request.Headers.Add(key, Headers[key]);
                }
            }

            //if (!string.IsNullOrEmpty(Token))
            //{
            //    request.Headers.Add("Authorization", $"Bearer {Token}");
            //}
            if (!(string.IsNullOrEmpty(Config.UserName) && string.IsNullOrEmpty(Config.Password)))
            {
                request.Credentials = new NetworkCredential(Config.UserName, Config.Password);
            }

            // 这里继续添加其它配置
            // ...

            request.Timeout = timeout;

            if (data != null && (method == "POST" || method == "PUT"))
            {
                request.ContentType = Config.ContentType;
                string content;
                if (Config.ContentType.StartsWith("application/json"))
                {
                    content = data.ToString();// JsonSerializer.Serialize(data, Config.JsonSerializerOptions);
                }
                else
                {
                    content = data.ToString();
                }
                byte[] dataBytes = Config.Encoding.GetBytes(content);

                using (Stream requestStream = await request.GetRequestStreamAsync())
                {
                    await requestStream.WriteAsync(dataBytes, 0, dataBytes.Length, cancellationToken);
                }
            }

            try
            {
                using (WebResponse response = await request.GetResponseAsync())
                using (Stream responseStream = response.GetResponseStream())
                using (StreamReader reader = new StreamReader(responseStream))
                {
                    string content = await reader.ReadToEndAsync();
                    byte[] bytes = Config.Encoding.GetBytes(content);

                    return HttpResponse.Success(bytes, encoding: Config.Encoding);
                }
            }
            catch (WebException ex)
            {
                await HandleExceptionAsync(ex);
                throw;
            }
            catch
            {
                throw;
            }
        }

        private async Task HandleExceptionAsync(WebException ex)
        {
            if (ex.Response != null)
            {
                using (var errorResponse = (HttpWebResponse)ex.Response)
                using (Stream responseStream = errorResponse.GetResponseStream())
                using (StreamReader reader = new StreamReader(responseStream))
                {
                    string errorContent = await reader.ReadToEndAsync();
                    throw new HttpRequestException($"HTTP请求失败。状态码：{(int)errorResponse.StatusCode} \n内容：{Environment.NewLine}{errorContent}");
                }
            }

            throw ex;
        }
    }
}
